<script lang="ts" module>
    export const TableContextKey = Symbol("TableContextKey");
    export const useTableContext = () => getContext(TableContextKey);
</script>

<script lang="ts">
    import { getContext, onDestroy, onMount, setContext, untrack } from "svelte";
    import { useClassList } from "../utils/useClassList";
    import type { ColumnProps, TableStore } from "./Table";
    import { addRemoveExpand, initColumns, initData, observerSizeChange, onResizeEnd, onResizeMove, onResizeStart, showHideChildren, sortHandler, updateScrollFixed } from "./utils";
    import { useStyle } from "../utils/useStyle";
    import { Spin } from "../Spin";
    import Head from "./Head.svelte";
    import Body from "./Body.svelte";
    import Summary from "./Summary.svelte";

    let { selectedRowKeys = $bindable([]), ...props } = $props();
    let wrap: any;
    const rowKey = props.rowKey ?? "id";
    let titleEl: HTMLDivElement | undefined;
    let footerEl: HTMLDivElement | undefined;
    let maxHeight = $state<number | undefined>(props.height === "fitContainer" ? undefined : (props.height as number));

    const { maxFixedLeft, minFixedRight } = initColumns(props.columns);
    let data: any[] = initData(props.data, rowKey);

    const store = $state<TableStore>({
        columns: [],
        columnsRows: [],
        data: [],
        showFixedLeft: false,
        showFixedRight: true,
        checkedAll: false,
        resizing: false,
        x: 0,
        posX: 0,
        startX: 0,
        resizeId: undefined,
        headerSize: {
            with: 0,
            height: 48,
        },
        summarySize: {
            with: 0,
            height: 0,
        },
        headerLeft: 0,
    });

    const calculateMaxHeight = () => {
        if (props.height === "fitContainer") {
            if (wrap) {
                const container = wrap.parentElement;
                const wrapHeight = container.getBoundingClientRect().height;
                const titleHeight = titleEl?.getBoundingClientRect().height ?? 0;
                const footerHeight = footerEl?.getBoundingClientRect().height ?? 0;
                const body = wrap.querySelector(".cm-table-body-wrap");
                const tolerenceHeight = wrapHeight - titleHeight - footerHeight;
                const realHeight = body?.getBoundingClientRect().height + store.headerSize.height + store.summarySize.height;

                if (realHeight > tolerenceHeight) {
                    return tolerenceHeight;
                } else {
                    return undefined;
                }
            }
            return undefined;
        }
        return props.height as number | undefined;
    };

    // 传入的data变化
    $effect(() => {
        const data = props.data;
        untrack(() => {
            const newData = initData(data, rowKey);
            queueMicrotask(() => {
                maxHeight = calculateMaxHeight();
            });
            store.data = newData;
            store.checkedAll = false;
        });
    });

    // 传入的columns变化
    $effect(() => {
        const columns = props.columns;
        untrack(() => {
            const { maxFixedLeft, minFixedRight, columnsRows, calcColumns } = initColumns(columns);
            store.columns = calcColumns;
            store.columnsRows = columnsRows;
            store.showFixedLeft = false;
            store.showFixedRight = true;
        });
    });

    // 选择框选择，并计算是否所有都选中，排除禁用行
    const onRowChecked = (row: any, checked: boolean) => {
        store.data.find((item) => item.id === row.id && (item._checked = checked));
        let status: boolean | string = false;
        let checkedNum = 0;
        let total = 0;
        const checkedRowKeys: KeyType[] = [];
        store.data.forEach((item: any) => {
            if (!item._disabled) {
                total++;
            }
            if (item._checked) {
                checkedRowKeys.push(item.id);
                checkedNum++;
                status = "indeterminate";
            }
        });
        if (checkedNum >= total) {
            status = true;
        }

        if (checkedRowKeys.join(",") !== selectedRowKeys.join(",")) {
            selectedRowKeys = checkedRowKeys;
        }

        store.checkedAll = status;
        props.onRowChecked && props.onRowChecked(row, checked);
    };

    $effect(() => {
        const rowKeys = selectedRowKeys;
        // data变更后也重新设置选择状态
        store.data;
        if (rowKeys && rowKeys.length > 0) {
            store.data.find((item) => rowKeys.includes(item.id) && !item._checked && (item._checked = true));
        } else {
            store.data.find((item) => item._checked && (item._checked = false));
        }
        let status: boolean | string = false;
        let checkedNum = 0;
        let total = 0;
        untrack(() => {
            store.data.forEach((item: any) => {
                if (!item._disabled) {
                    total++;
                }
                if (item._checked) {
                    checkedNum++;
                    status = "indeterminate";
                }
            });
        });
        if (checkedNum >= total) {
            status = true;
        }
        store.checkedAll = status;
    });

    // 头部选择框选中事件,禁用的选择框不进行响应
    const onHeadChecked = (checked: boolean) => {
        store.checkedAll = checked;
        // store.data.find((item) => (checked ? !item._disabled && !item._checked : !item._disabled && item._checked) && (item._checked = checked));
        store.data.forEach((item) => {
            if (!item._disabled) {
                item._checked = checked;
            }
        });
        const checkedRowKeys: KeyType[] = [];
        const rows = store.data.filter((item) => {
            if (item._checked) {
                checkedRowKeys.push(item.id);
            }
            return item._checked;
        });
        if (checkedRowKeys.join(",") !== selectedRowKeys.join(",")) {
            selectedRowKeys = checkedRowKeys;
        }
        props.onCheckedAll && props.onCheckedAll(rows);
    };

    // 点击排序执行
    const onSort = (column: ColumnProps, sortType: ColumnProps["sortType"]) => {
        sortHandler(store, column, sortType);

        if (props.onSort) {
            props.onSort(column, column.sortType);
        }
    };

    // 树形展开收缩
    const onShowChildren = (row: any) => {
        showHideChildren(store, row);
    };

    // 展开自定义内容
    const onExpand = (column: ColumnProps, row: any) => {
        addRemoveExpand(store, column, row, rowKey);
    };

    // resize开始
    const onDragStart = (column: ColumnProps, e: any) => {
        onResizeStart(store, column, e);

        document.addEventListener("mousemove", onDragMove, false);
        document.addEventListener("mouseup", onDragEnd, false);
    };
    // resize鼠标移动
    const onDragMove = (e: any) => {
        onResizeMove(store, e);
    };
    // resize停止
    const onDragEnd = () => {
        document.removeEventListener("mousemove", onDragMove);
        document.removeEventListener("mouseup", onDragEnd);
        onResizeEnd(store, wrap);
    };

    export const getAllChecked = () => {
        return store.data.filter((item: any) => {
            return item._checked;
        });
    };

    export const setChecked = (id: string | number, checked: boolean) => {
        const row: any = store.data.find((item: any) => {
            item.id === id;
        });
        onRowChecked(row, checked);
    };

    export const checkAll = (checked: boolean) => {
        onHeadChecked(checked);
    };

    const onInitColumnWidth = (index: number, width: number) => {
        store.columns[index]._width = width;
    };
    const onResizeHeader = (width: number, height: number) => {
        store.headerSize = { width, height };
    };
    const onResizeSummary = (width: number, height: number) => {
        store.summarySize = { width, height };
    };
    const onScrollBody = (scrollLeft: number, clientWidth: number, scrollWidth: number) => {
        updateScrollFixed(maxFixedLeft, minFixedRight, store, scrollLeft, clientWidth, scrollWidth);
        if (store.headerLeft !== scrollLeft) {
            store.headerLeft = scrollLeft;
        }
    };

    onMount(() => {
        const body = wrap.querySelector(".cm-table-body");
        const ro = new ResizeObserver((entries) => {
            entries.forEach((entry) => {
                observerSizeChange(store, wrap);
                setTimeout(() => {
                    onScrollBody(body.scrollLeft, body.clientWidth, body.scrollWidth);
                });
            });
        });
        ro.observe(body);
        let ro2: any;
        const parent = wrap.parentElement;
        if (props.height === "fitContainer") {
            ro2 = new ResizeObserver((entries) => {
                entries.forEach((entry) => {
                    maxHeight = calculateMaxHeight();
                });
            });
            ro2.observe(parent);
        }
        onDestroy(() => {
            ro.unobserve(body);
            ro2?.unobserve(parent);
        });
    });

    // resize 辅助线条样式
    const resizeStyle = $derived(
        useStyle(
            {},
            {
                display: store.resizing ? "block" : "none",
                left: store.posX + "px",
            },
        ),
    );

    const classList = $derived(
        useClassList(props, "cm-table-wrap", {
            "cm-table-border": props.border,
            "cm-table-stripe": props.stripe,
            "cm-table-small": props.size === "small",
            "cm-table-with-title": props.title,
            "cm-table-with-footer": props.footer,
            "cm-table-with-summary": props.showSummary,
            "cm-table-resizing": store.resizing,
        }),
    );

    const styles = $derived(
        useStyle(props, {
            "max-height": maxHeight ? `${maxHeight}px` : "",
        }),
    );
    const isSticky = $derived(!!props.height);

    setContext(TableContextKey, {
        onRowChecked,
        onHeadChecked,
        onSort,
        onShowChildren,
        onExpand,
        onDragStart,
        rowKey,
        border: props.border,
        spanMethod: props.spanMethod,
        empty: props.empty,
        expandedRowRender: props.expandedRowRender,
    });
</script>

<div class={classList} bind:this={wrap}>
    <div class="cm-table-resize-helper" style={resizeStyle}></div>
    <div class="cm-table-loading"></div>
    {#if props.loading}
        <Spin type="dot" title={props.loadingText || ""} />
    {/if}
    {#if props.title}
        <div class="cm-table-title" bind:this={titleEl}>
            {#if typeof props.title === "function"}
                {@render props.title()}
            {:else}
                {props.title}
            {/if}
        </div>
    {/if}
    <div class="cm-table" style={styles}>
        {#if props.showHeader ?? true}
            <Head data={store} sticky={isSticky} {onInitColumnWidth} {onResizeHeader} virtual={props.virtual} />
        {/if}
        <Body data={store} onScroll={onScrollBody} height={maxHeight} virtual={props.virtual} {rowKey} />
        {#if props.showSummary}
            <Summary data={store} {onResizeSummary} summaryMethod={props.summaryMethod} />
        {/if}
    </div>
    {#if props.footer}
        <div class="cm-table-footer" bind:this={footerEl}>
            {#if typeof props.footer === "function"}
                {@render props.footer()}
            {:else}
                {props.footer}
            {/if}
        </div>
    {/if}
</div>
